package com.example.latch;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/***
 *
 * @className: DbLeaderLatch
 * @author: chentao
 * @date: 2025/8/19 8:30
 * @Version: 1.0
 * @description:
 */
public class DbLeaderLatch implements LeaderLatch {

    Logger logger = LoggerFactory.getLogger(DbLeaderLatch.class);

    private ScheduledExecutorService scheduledExecutorService;

    private Connection connection;

    private PreparedStatement preparedStatement;

    private LeaderLatchRun leaderLatchRun;

    private int maxCorePoolSize = 5;

    private int maxDelayTime = 10;

    private volatile boolean isStop = false;

    private String sql = "select * from `sys_lock` where id = 1 for update";

    public DbLeaderLatch(Connection connection, LeaderLatchRun leaderLatchRun) {
        this.connection = connection;
        this.leaderLatchRun = leaderLatchRun;
    }

    public DbLeaderLatch(String sql, Connection connection, LeaderLatchRun leaderLatchRun) {
        this.sql = sql;
        this.connection = connection;
        this.leaderLatchRun = leaderLatchRun;
    }

    public DbLeaderLatch(int maxDelayTime, int maxCorePoolSize, String sql, Connection connection, LeaderLatchRun leaderLatchRun) {
        this.maxDelayTime = maxDelayTime;
        this.maxCorePoolSize = maxCorePoolSize;
        this.sql = sql;
        this.connection = connection;
        this.leaderLatchRun = leaderLatchRun;
    }

    @Override
    public void start() {
        this.scheduledExecutorService = new ScheduledThreadPoolExecutor(maxCorePoolSize);
        this.scheduledExecutorService.execute(new Runnable() {
            @Override
            public void run() {
                while (!isStop) {
                    try {
                        connection.setAutoCommit(false);
                        preparedStatement = connection.prepareStatement(sql);
                        ResultSet resultSet = preparedStatement.executeQuery();
                        if (resultSet.next()) {
                            leaderLatchRun.run();
                            leaseActive();
                            break;
                        }
                    } catch (SQLException exx) {
                        logger.info("Caught exception while executing sql statement {}", exx.getMessage());
                        if (null != preparedStatement) {
                            try {
                                preparedStatement.close();
                            } catch (SQLException e) {
                                logger.error(e.getMessage());
                            }
                        }
                    } finally {
                        if (null != preparedStatement) {
                            try {
                                preparedStatement.close();
                            } catch (SQLException e) {
                                logger.error(e.getMessage());
                            }
                        }
                    }
                }
            }
        });
    }

    @Override
    public void stop() {
        this.isStop = true;
        if (null != preparedStatement) {
            try {
                preparedStatement.close();
            } catch (SQLException e) {
                logger.error(e.getMessage());
            }
        }
        if (null != connection) {
            try {
                connection.rollback();
            } catch (SQLException e1) {
                logger.debug("Caught exception during rollback on connection");
            }
            try {
                connection.setAutoCommit(true);
            } catch (SQLException e) {
                logger.debug("Caught exception during auto commit on connection");
            }
            try {
                connection.close();
            } catch (SQLException e1) {
                logger.debug("Caught exception while closing connection");
            }
            connection = null;
        }
        this.scheduledExecutorService.shutdownNow();
    }

    @Override
    public void leaseActive() {
        this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                PreparedStatement preparedStatement = null;
                try {
                    preparedStatement = connection.prepareStatement("select 1");
                    preparedStatement.execute();
                } catch (Exception e) {
                    logger.error("Caught exception while executing sql statement {}", e.getMessage());
                } finally {
                    if (null != preparedStatement) {
                        try {
                            preparedStatement.close();
                        } catch (SQLException e) {
                            logger.error(e.getMessage());
                        }
                    }
                }
            }
        }, 0, maxDelayTime, TimeUnit.SECONDS);
    }

    public int getMaxCorePoolSize() {
        return maxCorePoolSize;
    }

    public void setMaxCorePoolSize(int maxCorePoolSize) {
        this.maxCorePoolSize = maxCorePoolSize;
    }

    public int getMaxDelayTime() {
        return maxDelayTime;
    }

    public void setMaxDelayTime(int maxDelayTime) {
        this.maxDelayTime = maxDelayTime;
    }
}

